﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Windows.Forms;
using ZedGraph;
using System.Globalization;
using SoftwareConsulting.Interfaces.BasicEntities;
using SoftwareConsulting.BI11.Studio.InternalServices.Properties;

namespace SoftwareConsulting.BI11.Studio.InternalServices
{
    public static class GeneralMethods
    {        
        /// <summary>
        /// Делегат операции сравнения чисел
        /// </summary>
        /// <param name="value1"></param>
        /// <param name="value2"></param>
        /// <returns></returns>
        public delegate bool CompareOperation(int value1, int value2);

        /// <summary>
        /// Возвращает флаг того, что значение 1 больше значения 2
        /// </summary>
        /// <param name="value1"></param>
        /// <param name="value2"></param>
        /// <returns></returns>
        public static bool IsMore(int value1, int value2)
        {
            return value1 > value2;
        }

        /// <summary>
        /// Возвращает флаг того, что значение 1 меньше значения 2
        /// </summary>
        /// <param name="value1"></param>
        /// <param name="value2"></param>
        /// <returns></returns>
        public static bool IsLess(int value1, int value2)
        {
            return value1 < value2;
        }

        /// <summary>
        /// Возвращает флаг того, что значение 1 больше или равно значению 2
        /// </summary>
        /// <param name="value1"></param>
        /// <param name="value2"></param>
        /// <returns></returns>
        public static bool IsMoreOrEquals(int value1, int value2)
        {
            return value1 >= value2;
        }

        /// <summary>
        /// Возвращает флаг того, что значение 1 меньше или равно значению 2
        /// </summary>
        /// <param name="value1"></param>
        /// <param name="value2"></param>
        /// <returns></returns>
        public static bool IsLessOrEquals(int value1, int value2)
        {
            return value1 <= value2;
        }

        /// <summary>
        /// Переводит целочисленное значение в логическое
        /// </summary>
        /// <param name="i"></param>
        /// <returns></returns>
        /// <remarks>>0 - false, все остальное - true</remarks>
        public static bool ToBoolean(this int integerToConvert)
        {            
            return integerToConvert != 0;
        }

        /// <summary>
        /// Переводит массив в строку с разделителями
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="list"></param>
        /// <param name="delimiter"></param>
        /// <returns></returns>
        public static string ToStringWithDelimiters<T>(this List<T> list, char delimiter)
        {
            string result = string.Empty;
            foreach (T item in list)
            {
                result += item.ToString() + delimiter;
            }
            int length = result.Length;
            if (length > 0)
                result = result.Substring(0, length - 1);
            return result;
        }

        /// <summary>
        /// Предупреждает разработчика, что он забыл вызвать Dispose для указанного объекта
        /// </summary>
        /// <param name="caller"></param>
        public static void NoDisposeWarning(this INamed caller)
        {
#if DEBUG
            AppDomain currentDomain = AppDomain.CurrentDomain;
            if (!currentDomain.IsFinalizingForUnload() && !Environment.HasShutdownStarted)
            {
                Trace.StartBlock("No dispose");
                string callerName = caller.Name;                
                Trace.Message("You forget to call dispose for '" + callerName + "' of '" + typeof(INamed).FullName + "'!");
                Trace.Message("This object has been disposed automatically from finalizer. It may cause problems in future.");
                Trace.Message("Consider design where 'dispose' is called manually.");
                Trace.EndBlock();
            }
#endif
        }

        /// <summary>
        /// Отображает ошибку пользователя
        /// </summary>
        /// <param name="errorText"></param>
        public static void ShowUserError(string errorText)
        {
            MessageBox.Show(errorText, Resources.BI_MESSAGE_ERROR_CAPTION, MessageBoxButtons.OK, MessageBoxIcon.Stop);
        }

        /// <summary>
        /// Отображает информационное сообщение
        /// </summary>
        /// <param name="infoText"></param>
        public static void ShowInformation(string infoText)
        {
            ShowInformation(infoText, Resources.BI_MESSAGE_INFORMATION_CAPTION);
        }

        /// <summary>
        /// Отображает информационное сообщение
        /// </summary>
        /// <param name="infoText"></param>
        public static void ShowInformation(string infoText, string captionText)
        {
            MessageBox.Show(infoText, captionText, MessageBoxButtons.OK, MessageBoxIcon.Information);
        }

        /// <summary>
        /// Отображает предупреждающее сообщение
        /// </summary>
        /// <param name="warningText"></param>
        public static DialogResult ShowWarning(string warningText)
        {
            return ShowWarning(warningText, MessageBoxButtons.OK);
        }

        /// <summary>
        /// Отображает предупреждающее сообщение
        /// </summary>
        /// <param name="warningText"></param>
        public static DialogResult ShowWarning(string warningText, MessageBoxButtons buttons)
        {
            return MessageBox.Show(warningText, Resources.BI_MESSAGE_WARNING_CAPTION, buttons, buttons == MessageBoxButtons.OK ? MessageBoxIcon.Warning : MessageBoxIcon.Question);
        }

        /// <summary>
        /// Выбирает цвет по его индексу
        /// </summary>
        /// <param name="index"></param>
        /// <returns></returns>
        public static Color GetColorByIndex(int index)
        {
            index = index % 15;
            switch (index)
            {
                case 0:
                    return Color.Red;
                case 1:
                    return Color.Blue;
                case 2:
                    return Color.Green;
                case 3:
                    return Color.Brown;
                case 4:
                    return Color.Pink;
                case 5:
                    return Color.Black;
                case 6:
                    return Color.Aqua;
                case 7:
                    return Color.Chocolate;
                case 8:
                    return Color.Gray;
                case 9:
                    return Color.Yellow;
                case 10:
                    return Color.Violet;
                case 11:
                    return Color.Silver;
                case 12:
                    return Color.Orange;
                case 13:
                    return Color.Navy;
                case 14:
                    return Color.Lime;
            }
            return Color.Khaki;
        }

        /// <summary>
        /// Выбирает символ на графике по его индексу
        /// </summary>
        /// <param name="index"></param>
        /// <returns></returns>
        public static int GetGraphSymbolByIndex(int index)
        {
            index = index % 10;
            switch (index)
            {
                case 0:
                    return (int)SymbolType.Circle;
                case 1:
                    return (int)SymbolType.Diamond;
                case 2:
                    return (int)SymbolType.Square;
                case 3:
                    return (int)SymbolType.Star;
                case 4:
                    return (int)SymbolType.Triangle;
                case 5:
                    return (int)SymbolType.TriangleDown;
                case 6:
                    return (int)SymbolType.XCross;
                case 7:
                    return (int)SymbolType.Plus;
                case 8:
                    return (int)SymbolType.HDash;
                case 9:
                    return (int)SymbolType.VDash;
            }
            return (int)SymbolType.Default;
        }        

        /// <summary>
        /// Корректирует задаваемое имя
        /// </summary>
        /// <param name="wantName"></param>
        /// <returns></returns>
        public static string TrimName(string wantName)
        {
            string gotName = wantName.Trim();
            bool badFlag = string.IsNullOrEmpty(gotName);
            foreach(char wrongSymbol in Constants.WrongNameSymbols)            
            {
                if (badFlag)
                    break;                
                if (gotName.Contains(wrongSymbol))
                {
                    badFlag = true;
                }
            }
            if (badFlag)
            {
                throw new BIIncorrectNameException(null);
            }
            return gotName;
        }

        /// <summary>
        /// Корректирует задаваемое имя, запрещая дополнительно использовать еще и точку
        /// </summary>
        /// <param name="wantName"></param>
        /// <returns></returns>
        /// <remarks>нужно для иерархии ситуаций, т.к. там нет
        /// ярлыков, и, соответственно, в контейнере правил
        /// использование точки в имени фрейма/слота и т.п.
        /// приведет к проблемам в распарсивании</remarks>
        public static string TrimNameFailOnPoint(string wantName)
        {
            string gotName = wantName.Trim();
            bool badFlag = string.IsNullOrEmpty(gotName);
            string denied = Constants.WrongNameSymbols.Insert(0, ".");
            foreach(char wrongSymbol in denied)            
            {
                if (badFlag)
                    break;                
                if (gotName.Contains(wrongSymbol))
                {
                    badFlag = true;
                }
            }
            if (badFlag)
            {
                throw new BIIncorrectNameException(null, denied);
            }
            return gotName;
        }        

        /// <summary>
        /// Выводит сообщение о невозможности пока что использовать данный функционал
        /// </summary>
        public static void UnderConstruction()
        {
            GeneralMethods.ShowInformation(Resources.BI_MESSAGE_UNDERCONSTRUCTION);            
        }

        /// <summary>
        /// Проверят является ли указанный объект строкой-NULLobject
        /// </summary>
        /// <param name="testedObject"></param>
        /// <returns></returns>
        public static bool IsNullString(object testedObject)
        {
            string str = testedObject as string;
            return str == Constants.NullDoesNotMatterString || str == Constants.NullNotDefinedString;
        }

        /// <summary>
        /// Проверяет является ли указанный объект NULLом
        /// </summary>
        /// <returns></returns>
        public static bool IsNull(object testedObject)
        {
            return ((testedObject == null) || IsNullString(testedObject));
        }

        /// <summary>
        /// Проверяет являются ли 2 массива одинаковыми (без учета порядков элементов)
        /// </summary>
        /// <param name="object1"></param>
        /// <param name="object2"></param>
        /// <returns></returns>
        public static bool SameArraysWithoutOrderChecking(object object1, object object2)
        {
            IList ar1 = object1 as IList;
            IList ar2 = object2 as IList;
            if ((ar1 != null) && (ar2 != null))
            {                
                int c1 = ar1.Count;
                int c2 = ar2.Count;
                if (c1 != c2)
                    return false;
                List<int> exceptedItems = new List<int>();
                for (int i = 0; i < c1; i++)
                {
                    object val1 = ar1[i];
                    int j = 0;
                    bool found = false;
                    while (j < c2)
                    {
                        if (!exceptedItems.Contains(j))
                        {
                            if (Identical(val1, ar2[j]))
                            {
                                exceptedItems.Add(j);
                                found = true;
                                break;
                            }
                        }
                        j++;
                    }
                    if (!found)
                        return false;
                }
                return true;
            }
            return false;
        }

        /// <summary>
        /// Проверяет идентичны ли два объекта
        /// </summary>
        /// <param name="object1"></param>
        /// <param name="obj2"></param>
        /// <returns>true - идентичны, false - нет</returns>
        public static bool Identical(object object1, object object2)
        {            
            Array ar1 = object1 as Array;
            Array ar2 = object2 as Array;
            if ((ar1 != null) && (ar2 != null))
            {                
                int c1 = ar1.Length;
                int c2 = ar2.Length;
                if (c1 != c2)
                    return false;
                for (int i = 0; i < c1; i++)
                {
                    if (!Identical(ar1.GetValue(i), ar2.GetValue(i)))
                        return false;
                }
                return true;
            }
            if (object1 is Guid)
            {
                object1 = ((Guid)object1).ToString();
            }
            if (object2 is Guid)
            {
                object2 = ((Guid)object2).ToString();
            }
            if ((object1 is short) || (object1 is int))
            {
                object1 = object1.ToString();
            }
            if ((object2 is short) || (object2 is int))
            {
                object2 = object2.ToString();
            }
            if (object1 is Enum)
            {
                object1 = ((int)object1).ToString(CultureInfo.CurrentCulture);
            }
            if (object2 is Enum)
            {
                object2 = ((int)object2).ToString(CultureInfo.CurrentCulture);
            }
            return (object1 == null) && (object2 == null) || (object1 != null) && (object1.Equals(object2));
        }

        /// <summary>
        /// Удаляет все нецифры из строки
        /// </summary>
        /// <param name="originalString"></param>
        /// <returns></returns>
        public static string RemoveNonnumericSymbolsFromString(string originalString)
        {
            string numbers = "0123456789";
            string ss = string.Empty;                        
            foreach(char ch in originalString)
            {
                string sc = ch.ToString();                
                if (numbers.Contains(sc))
                {                
                    ss = ss + sc;
                }
            }
            return ss;
        }

        /// <summary>
        /// Возвращает число, содержащееся в конце строки
        /// </summary>
        /// <param name="originalString">строка</param>
        /// <param name="numberPosition">позиция числа в строке</param>
        /// <param name="parsed">влезло ли число в допустимые рамки integer'а</param>
        /// <returns>число, если такового нет, то -1</returns>        
        public static int NumberOnEnd(string originalString, out int numberPosition, out bool parsed)
        {
            parsed = false;
            string num = string.Empty;
            while ((originalString.EndsWith("0",StringComparison.CurrentCulture))
                || (originalString.EndsWith("1", StringComparison.CurrentCulture))
                || (originalString.EndsWith("2", StringComparison.CurrentCulture))
                || (originalString.EndsWith("3", StringComparison.CurrentCulture))
                || (originalString.EndsWith("4", StringComparison.CurrentCulture))
                || (originalString.EndsWith("5", StringComparison.CurrentCulture))
                || (originalString.EndsWith("6", StringComparison.CurrentCulture))
                || (originalString.EndsWith("7", StringComparison.CurrentCulture))
                || (originalString.EndsWith("8", StringComparison.CurrentCulture))
                || (originalString.EndsWith("9", StringComparison.CurrentCulture)))
            {
                num = originalString.Substring(originalString.Length - 1, 1) + num;
                originalString = originalString.Substring(0, originalString.Length - 1);                
            }
            int result = -1;
            numberPosition = originalString.Length;                            
            parsed = string.IsNullOrEmpty(num) || 
                int.TryParse(num, NumberStyles.Integer, CultureInfo.CurrentCulture, out result);            
            return result;
        }
    }
}
